package org.myspringframework.context.annotation;

import org.myspringframework.beans.BeansException;
import org.myspringframework.beans.factory.BeanFactory;
import org.myspringframework.beans.factory.ConfigurableListableBeanFactory;
import org.myspringframework.beans.factory.aware.BeanFactoryAware;
import org.myspringframework.beans.factory.config.beandefinition.PropertyValues;
import org.myspringframework.beans.factory.config.postprocessor.InstantiationAwareBeanPostProcessor;
import org.myspringframework.utils.ClassUtils;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.stream.Collectors;

//依赖注入相关beanpostprocessor
//扫描自定义注解@Autowried @Qualifier @Value,进行依赖注入
public class AutowiredAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {

    private ConfigurableListableBeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
    }

    //在属性注入之前进行@Autowried @Qualifier @Value的依赖注入
    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        // 1. 处理注解 @Value
        Class<?> clazz = bean.getClass();
        //这里需要注意一点因为我们在 AbstractAutowireCapableBeanFactory 类中使用的是 CglibSubclassingInstantiationStrategy 进行类的创建，
        //所以在 AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues 中需要判断是否为 CGlib 创建对象，否则是不能正确拿到类信息的。
        clazz = ClassUtils.isCglibProxyClass(clazz) ? clazz.getSuperclass() : clazz;

        Field[] declaredFields = clazz.getDeclaredFields();

        //遍历字段查看是否有@Value注解
        for (Field field : declaredFields) {
            Value valueAnnotation = field.getAnnotation(Value.class);
            if (null != valueAnnotation) {
                //有@Value
                String value = valueAnnotation.value();
                //使用beanFactory中的字符串解析器对@Value的字符串值进行解析
                value = beanFactory.resolveEmbeddedValue(value);

                try{
                    //进行类型转换 并 反射赋值
                    Class<?> fieldType = field.getType();
                    field.setAccessible(true);
                    if (fieldType==int.class||fieldType==Integer.class){
                        field.setInt(bean,Integer.parseInt((String)value));
                    }else if (fieldType== Double.class||fieldType== double.class){
                        field.setDouble(bean,Double.valueOf((String) value));
                    }else if (fieldType==Boolean.class||fieldType==boolean.class){
                        field.setBoolean(bean,Boolean.getBoolean((String) value));
                    }
                }catch (Exception e){
                    System.out.println("@Value属性字段设值失败:"+field+","+value);
                    e.printStackTrace();
                }
            }
        }

        // 2. 处理注解 @Autowired
        for (Field field : declaredFields) {
            Autowired autowiredAnnotation = field.getAnnotation(Autowired.class);
            if (null != autowiredAnnotation) {
                //有@Autowired注解
                Class<?> fieldType = field.getType();
                String dependentBeanName = null;

                //判断是否有@Qualifier
                Qualifier qualifierAnnotation = field.getAnnotation(Qualifier.class);
                Object dependentBean = null;
                if (null != qualifierAnnotation) {

                    //1.有@Qualifier，beanName为@Qualifier中的值
                    dependentBeanName = qualifierAnnotation.value();
                    dependentBean = beanFactory.getBean(dependentBeanName, fieldType);

                } else {

                    //2.没有@Qualifier,先根据类型查找bean
                    Map<String, ?> beansOfType = beanFactory.getBeansOfType(fieldType);
                    if (beansOfType.size()==0){
                        throw new BeansException("找不到类型为"+fieldType+"的bean");
                    }else if (beansOfType.size()>1){
                        //有多个相同类型的bean，再根据字段名称匹配
                        dependentBeanName=field.getName();
                        Object o = beansOfType.get(dependentBeanName);
                        if (o==null){
                            throw new BeansException("有多个类型为"+fieldType+"的bean");
                        }else {
                            dependentBean=o;
                        }
                    }else{
                        //只有一个匹配该字段类型的bean
                        dependentBean=beansOfType.values().stream().collect(Collectors.toList()).get(0);
                    }
                }

                //反射为字段赋值
                try{
                    field.setAccessible(true);
                    field.set(bean,dependentBean);
                }catch (Exception e){
                    System.out.println("@Autowried属性字段设值失败:"+field+","+dependentBean);
                    e.printStackTrace();
                }
            }
        }

        return pvs;
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        return null;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}